﻿using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using StyleCop.CSharp;

namespace Jsl.StyleCop.CSharp
{
    [TestClass]
    public class TestVariableUsageInformation
    {
        [TestMethod]
        public void The_scope_of_a_variable_declaration_at_method_level_is_the_method()
        {
            var method = MethodCollectorRule.GetMethod("int variable;");

            // Get the variable in the assignment.
            var declarationStatement = (VariableDeclarationStatement)method.ChildStatements.First();
            var variableExpression = declarationStatement.Declarators.First().Identifier;
            VariableUsageInformation variableUsage = variableExpression.GetVariableUsage();

            Assert.AreSame(method, variableUsage.SmallestVariableScope());
        }

        [TestMethod]
        public void The_scope_should_be_the_method_if_at_top_level()
        {
            var method = MethodCollectorRule.GetMethod(@"
int variable;
variable = 5;");

            // Get the variable in the assignment.
            ExpressionStatement expressionStatement = (ExpressionStatement)method.ChildStatements.ElementAt(1);
            AssignmentExpression assignmentExpression = (AssignmentExpression)expressionStatement.Expression;
            LiteralExpression variableExpression = (LiteralExpression)assignmentExpression.LeftHandSide;
            VariableUsageInformation variableUsage = variableExpression.GetVariableUsage();

            Assert.AreSame(method, variableUsage.SmallestVariableScope());
        }

        [TestMethod]
        public void Foreach_variable_has_foreach_as_the_scope()
        {
            var method = MethodCollectorRule.GetMethod(@"
foreach (int variable in collection)
{
}");

            ForeachStatement foreachStatement = (ForeachStatement)method.ChildStatements.First();
            VariableDeclaratorExpression variableDeclarator = foreachStatement.Variable.Declarators.First();
            VariableUsageInformation variableUsage = variableDeclarator.Identifier.GetVariableUsage();

            Assert.AreSame(foreachStatement, variableUsage.SmallestVariableScope());
        }

        [TestMethod]
        public void For_variable_has_for_as_the_scope()
        {
            var method = MethodCollectorRule.GetMethod(@"
for (int variable = 0; variable < 10; variable++)
{
}");

            ForStatement forStatement = (ForStatement)method.ChildStatements.First();
            VariableDeclarationExpression variableDeclaration = (VariableDeclarationExpression)forStatement.Initializers.First();
            VariableDeclaratorExpression variableDeclarator = variableDeclaration.Declarators.First();
            VariableUsageInformation variableUsage = variableDeclarator.Identifier.GetVariableUsage();

            Assert.AreSame(forStatement, variableUsage.SmallestVariableScope());
        }

        [TestMethod]
        public void For_variable_used_in_condition_has_for_as_the_scope()
        {
            var method = MethodCollectorRule.GetMethod(@"
for (int variable = 0; variable < 10; variable++)
{
}");

            ForStatement forStatement = (ForStatement)method.ChildStatements.First();
            RelationalExpression conditionExpression = (RelationalExpression)forStatement.Condition;
            LiteralExpression variableReference = (LiteralExpression)conditionExpression.LeftHandSide;
            VariableUsageInformation variableUsage = variableReference.GetVariableUsage();

            Assert.AreSame(forStatement, variableUsage.SmallestVariableScope());
        }

        [TestMethod]
        public void For_each_collection_variable_block_as_the_container()
        {
            var method = MethodCollectorRule.GetMethod(@"
List<int> variable = new List<int>();
foreach (int index in variable)
{
}");

            ForeachStatement foreachStatement = (ForeachStatement)method.ChildStatements.ElementAt(1);
            LiteralExpression variableReference = (LiteralExpression)foreachStatement.Item;
            VariableUsageInformation variableUsage = variableReference.GetVariableUsage();

            Assert.AreSame(method, variableUsage.SmallestVariableScope());
        }

        [TestMethod]
        public void Variable_inside_foreach_has_foreach_as_scope()
        {
            var method = MethodCollectorRule.GetMethod(@"
int variable = 5;
foreach (int index in collection)
    return variable;");

            ForeachStatement foreachStatement = (ForeachStatement)method.ChildStatements.ElementAt(1);
            ReturnStatement returnStatement = (ReturnStatement)foreachStatement.EmbeddedStatement;
            LiteralExpression variableReference = (LiteralExpression)returnStatement.ReturnValue;
            VariableUsageInformation variableUsage = variableReference.GetVariableUsage();

            Assert.AreSame(foreachStatement, variableUsage.SmallestVariableScope());
        }

        [TestMethod]
        public void Variable_inside_block_inside_foreach_has_foreach_as_scope()
        {
            var method = MethodCollectorRule.GetMethod(@"
int variable = 5;
foreach (int index in collection)
{
    return variable;
}");

            ForeachStatement foreachStatement = (ForeachStatement)method.ChildStatements.ElementAt(1);
            BlockStatement blockStatement = (BlockStatement)foreachStatement.EmbeddedStatement;
            ReturnStatement returnStatement = (ReturnStatement)blockStatement.ChildStatements.First();
            LiteralExpression variableReference = (LiteralExpression)returnStatement.ReturnValue;
            VariableUsageInformation variableUsage = variableReference.GetVariableUsage();

            Assert.AreSame(foreachStatement, variableUsage.SmallestVariableScope());
        }

        [TestMethod]
        public void Catch_clause_variable_has_catch_as_the_container()
        {
            var method = MethodCollectorRule.GetMethod(@"
try
{
}
catch (Exception ex)
{
}");

            TryStatement tryStatement = (TryStatement)method.ChildStatements.First();
            CatchStatement catchStatement = tryStatement.CatchStatements.First();
            VariableDeclarationExpression variableDeclaration = (VariableDeclarationExpression)catchStatement.CatchExpression;
            VariableDeclaratorExpression variableDeclarator = variableDeclaration.Declarators.First();
            VariableUsageInformation variableUsage = variableDeclarator.Identifier.GetVariableUsage();

            Assert.AreSame(catchStatement, variableUsage.SmallestVariableScope());
        }
    }
}